package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.list.repository.GoodsRepository;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseAttrInfo;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.*;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ElasticsearchRestTemplate es;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private GoodsRepository goodsRepository;
@Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public void lowerGoods(Long skuId) {
        goodsRepository.deleteById(skuId);

    }

    @Override
    public void upperGoods(Long skuId) {
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());
        BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
        List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);


        Goods goods = new Goods();
        goods.setId(skuId);
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
        goods.setTitle(skuInfo.getSkuName());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        goods.setCreateTime(new Date());
        goods.setTmId(skuInfo.getTmId());

        if (trademark != null) {

            goods.setTmName(trademark.getTmName());
            goods.setTmLogoUrl(trademark.getLogoUrl());
        }

        if (categoryView != null) {
            goods.setCategory1Name(categoryView.getCategory1Name());
            goods.setCategory2Name(categoryView.getCategory2Name());
            goods.setCategory3Name(categoryView.getCategory3Name());
            goods.setCategory1Id(categoryView.getCategory1Id());
            goods.setCategory2Id(categoryView.getCategory2Id());
            goods.setCategory3Id(categoryView.getCategory3Id());
        }

        goods.setHotScore(0L);

        ArrayList<SearchAttr> searchAttrs = new ArrayList<>();

        if (!CollectionUtils.isEmpty(attrList)) {
            for (BaseAttrInfo item : attrList) {
                SearchAttr searchAttr = new SearchAttr();
                searchAttr.setAttrId(item.getId());
                searchAttr.setAttrName(item.getAttrName());
                searchAttr.setAttrValue(item.getAttrValueList().get(0).getValueName());
                searchAttrs.add(searchAttr);
            }
        }

        goods.setAttrs(searchAttrs);

        goodsRepository.save(goods);
    }

    @Override
    public SearchResponseVo search(SearchParam searchParam) throws IOException {
        SearchRequest searchRequest = this.makeSearchRequest(searchParam);
        //  查询之后的结果集
        SearchResponse searchResponse = this.restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //  将数据结果集进行转换
        SearchResponseVo searchResponseVo = parseSearchResult(searchResponse);

        searchResponseVo.setPageNo(searchParam.getPageNo());
        searchResponseVo.setPageSize(searchParam.getPageSize());

        searchResponseVo.setTotalPages((searchResponseVo.getTotal()+searchResponseVo.getPageSize()-1)/ searchResponseVo.getPageSize());



        return searchResponseVo;
    }

    private SearchResponseVo parseSearchResult(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        SearchHits hits = searchResponse.getHits();
        //商品
        List<Goods> goods = new ArrayList<>();
        if (hits != null) {
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                Goods good = JSON.parseObject(sourceAsString, Goods.class);
                HighlightField title = hit.getHighlightFields().get("title");
                if (title != null && title.getFragments() != null && title.getFragments() != null && title.getFragments().length > 0) {
                    String s = title.getFragments()[0].toString();
                    good.setTitle(s);
                }
                goods.add(good);
            }
        }
        searchResponseVo.setGoodsList(goods);
/////////////品牌
        final Aggregations aggregations = searchResponse.getAggregations();
        final Aggregation agg_tmId = aggregations.get("agg_tmId");
        List<SearchResponseTmVo> TmList = ((ParsedLongTerms) agg_tmId).getBuckets().stream().map((bucket) -> {
            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();
            searchResponseTmVo.setTmId(bucket.getKeyAsNumber().longValue());

            String agg_tmName = ((ParsedStringTerms) bucket.getAggregations()
                                                           .get("agg_tmName")).getBuckets()
                                                                              .get(0)
                                                                              .getKeyAsString();
            searchResponseTmVo.setTmName(agg_tmName);

            String agg_tmLogo = ((ParsedStringTerms) bucket.getAggregations().get("agg_tmLogo")).getBuckets()
                                                                                                .get(0)
                                                                                                .getKeyAsString();
            searchResponseTmVo.setTmLogoUrl(agg_tmLogo);

            return searchResponseTmVo;
        }).collect(Collectors.toList());
        searchResponseVo.setTrademarkList(TmList);

        Aggregation agg_attr = aggregations.get("agg_attr");
        Aggregation agg_attrId = ((ParsedNested) agg_attr).getAggregations().get("agg_attrId");


        List<SearchResponseAttrVo> attrList = ((ParsedLongTerms) agg_attrId).getBuckets().stream().map(bucket -> {
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            searchResponseAttrVo.setAttrId(bucket.getKeyAsNumber().longValue());
            String agg_attrName = ((ParsedStringTerms) bucket.getAggregations().get("agg_attrName")).getBuckets()
                                                                                                    .get(0)
                                                                                                    .getKeyAsString();
            searchResponseAttrVo.setAttrName(agg_attrName);
            List<String> agg_attrValue = ((ParsedStringTerms) bucket.getAggregations()
                                                                    .get("agg_attrValue")).getBuckets()
                                                                                          .stream()
                                                                                          .map(Terms.Bucket::getKeyAsString)
                                                                                          .collect(Collectors.toList());
            searchResponseAttrVo.setAttrValueList(agg_attrValue);

            return searchResponseAttrVo;
        }).collect(Collectors.toList());
        searchResponseVo.setAttrsList(attrList);

        searchResponseVo.setTotal(hits.totalHits);


        return searchResponseVo;
    }

    private SearchRequest makeSearchRequest(SearchParam searchParam) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        searchSourceBuilder.query(boolQueryBuilder);
        //分类
        if (!StringUtils.isEmpty(searchParam.getCategory1Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory2Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", searchParam.getCategory2Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory3Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id()));
        }
        //品牌    trademark=2:苹果
        if (!StringUtils.isEmpty(searchParam.getTrademark())) {
            String[] split = searchParam.getTrademark().split(":");
            if (split.length == 2) {
                TermQueryBuilder tmId = QueryBuilders.termQuery("tmId", Long.valueOf(split[0]));
                TermQueryBuilder tmName = QueryBuilders.termQuery("tmName", split[1]);
                boolQueryBuilder.filter(tmId).filter(tmName);
            }
        }
        //关键字
        if (!StringUtils.isEmpty(searchParam.getKeyword())) {
            String keyword = searchParam.getKeyword();
            searchSourceBuilder.query(QueryBuilders.matchQuery("title", keyword));
        }
        //平台属性   106:苹果手机:手机一级
        if (searchParam.getProps() != null && searchParam.getProps().length != 0) {
            String[] props = searchParam.getProps();
            for (String prop : props) {
                String[] split = prop.split(":");
                if (split.length == 3) {
                    BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
                    nestedBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", Long.valueOf(split[0])));
                    nestedBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrName", split[2]));
                    nestedBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                    NestedQueryBuilder attrs = QueryBuilders.nestedQuery("attrs", nestedBoolQueryBuilder, ScoreMode.None);
                    boolQueryBuilder.filter(attrs);
                }
            }
        }
        //排序 前端传递过来的数据 ：  1:asc 1:desc  2:asc  2:desc
        if (!StringUtils.isEmpty(searchParam.getOrder())) {
            String order = searchParam.getOrder();
            String[] split = order.split(":");
            if (split.length == 2) {
                if ("1".equals(split[0])) {
                    searchSourceBuilder.sort("hotScore", "asc".equals(split[1].toLowerCase(Locale.ROOT)) ? SortOrder.ASC : SortOrder.DESC);
                } else if ("2".equals(split[0])) {
                    searchSourceBuilder.sort("price", "desc".equals(split[1].toLowerCase(Locale.ROOT)) ? SortOrder.DESC : SortOrder.ASC);
                }
            }
        } else {
            searchSourceBuilder.sort("hotScore", SortOrder.DESC);
        }
        //品牌聚合
        TermsAggregationBuilder tmId = AggregationBuilders.terms("agg_tmId").field("tmId");
        tmId.subAggregation(AggregationBuilders.terms("agg_tmLogo").field("tmLogoUrl"));
        tmId.subAggregation(AggregationBuilders.terms("agg_tmName").field("tmName"));
        searchSourceBuilder.aggregation(tmId);

        //平台属性聚合
        /**
         *       "aggs": {
         *         "agg_attrId": {
         *           "terms": {
         *             "field": "attrs.attrId",
         *             "size": 10
         *           },
         *           "aggs": {
         *             "agg_attrName": {
         *               "terms": {
         *                 "field": "attrs.attrName",
         *                 "size": 10
         *               }
         *             },
         *             "agg_attrValue": {
         *               "terms": {
         *                 "field": "attrs.attrValue",
         *                 "size": 10
         *               }
         *             }
         *           }
         */
        NestedAggregationBuilder nested = AggregationBuilders.nested("agg_attr", "attrs");

        TermsAggregationBuilder agg_attrId = AggregationBuilders.terms("agg_attrId").field("attrs.attrId");


        agg_attrId.subAggregation(AggregationBuilders.terms("agg_attrName").field("attrs.attrName"));
        agg_attrId.subAggregation(AggregationBuilders.terms("agg_attrValue").field("attrs.attrValue"));

        nested.subAggregation(agg_attrId);

        searchSourceBuilder.aggregation(nested);
//高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        HighlightBuilder titleHigh = highlightBuilder.field("title")
                                                     .preTags("<span style=color:red>")
                                                     .postTags("</span>");
        searchSourceBuilder.highlighter(titleHigh);

////////////////////////
        int from = (searchParam.getPageNo()-1)*searchParam.getPageSize();

        searchSourceBuilder.from(from);
        searchSourceBuilder.size(searchParam.getPageSize());

        SearchRequest searchRequest = new SearchRequest("goods");
        searchRequest.source(searchSourceBuilder);
        System.out.println(searchSourceBuilder);
        return searchRequest;

    }
}
